home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr48 / unzipsrc.zip / UNZ.PAS < prev    next >
Pascal/Delphi Source File  |  1993-04-16  |  20KB  |  852 lines

  1.  
  2. (*
  3.  * Copyright 1987, 1989 Samuel H. Smith;  All rights reserved
  4.  *
  5.  * This is a component of the ProDoor System.
  6.  * Do not distribute modified versions without my permission.
  7.  * Do not remove or alter this notice or any other copyright notice.
  8.  * If you use this in your own program you must distribute source code.
  9.  * Do not use any of this in a commercial product.
  10.  *
  11.  *)
  12.  
  13. (*
  14.  * UnZip - A simple zipfile extract utility
  15.  *
  16.  *)
  17.  
  18. {$I+}    {I/O checking}
  19. {$N-}    {Numeric coprocessor}
  20. {$V-}    {Relaxes string typing}
  21. {$B-}    {Boolean complete evaluation}
  22. {$S-}    {Stack checking}
  23. {$R-}    {Range checking}
  24. {$D+}    {Global debug information}
  25. {$L+}    {Local debug information}
  26.  
  27. {$M 5000,0,0} {minstack,minheap,maxheap}
  28.  
  29. program UnZip;
  30.  
  31. Uses
  32.    Dos, Mdosio;
  33.  
  34. const
  35.    version = 'UnZip:  Zipfile Extract v1.1 of 03-06-89;  (C) 1989 S.H.Smith';
  36.  
  37.  
  38.  
  39. (*
  40.  * ProZip2.int - ZIP file interface library      (2-15-89 shs)
  41.  *
  42.  * Data declarations for the archive text-view functions.
  43.  *
  44.  *)
  45.  
  46. (* ----------------------------------------------------------- *)
  47. (*
  48.  * ZIPfile layout declarations
  49.  *
  50.  *)
  51.  
  52. type
  53.    signature_type = longint;
  54.  
  55. const
  56.    local_file_header_signature = $04034b50;
  57.  
  58. type
  59.    local_file_header = record
  60.       version_needed_to_extract:    word;
  61.       general_purpose_bit_flag:     word;
  62.       compression_method:           word;
  63.       last_mod_file_time:           word;
  64.       last_mod_file_date:           word;
  65.       crc32:                        longint;
  66.       compressed_size:              longint;
  67.       uncompressed_size:            longint;
  68.       filename_length:              word;
  69.       extra_field_length:           word;
  70.    end;
  71.  
  72. const
  73.    central_file_header_signature = $02014b50;
  74.  
  75. type
  76.    central_directory_file_header = record
  77.       version_made_by:                 word;
  78.       version_needed_to_extract:       word;
  79.       general_purpose_bit_flag:        word;
  80.       compression_method:              word;
  81.       last_mod_file_time:              word;
  82.       last_mod_file_date:              word;
  83.       crc32:                           longint;
  84.       compressed_size:                 longint;
  85.       uncompressed_size:               longint;
  86.       filename_length:                 word;
  87.       extra_field_length:              word;
  88.       file_comment_length:             word;
  89.       disk_number_start:               word;
  90.       internal_file_attributes:        word;
  91.       external_file_attributes:        longint;
  92.       relative_offset_local_header:    longint;
  93.    end;
  94.  
  95. const
  96.    end_central_dir_signature = $06054b50;
  97.  
  98. type
  99.    end_central_dir_record = record
  100.       number_this_disk:                         word;
  101.       number_disk_with_start_central_directory: word;
  102.       total_entries_central_dir_on_this_disk:   word;
  103.       total_entries_central_dir:                word;
  104.       size_central_directory:                   longint;
  105.       offset_start_central_directory:           longint;
  106.       zipfile_comment_length:                   word;
  107.    end;
  108.  
  109.  
  110.  
  111. (* ----------------------------------------------------------- *)
  112. (*
  113.  * input file variables
  114.  *
  115.  *)
  116.  
  117. const
  118.    uinbufsize = 512;    {input buffer size}
  119. var
  120.    zipeof:      boolean;
  121.    csize:       longint;
  122.    cusize:      longint;
  123.    cmethod:     integer;
  124.    ctime:       word;
  125.    cdate:       word;
  126.    inbuf:       array[1..uinbufsize] of byte;
  127.    inpos:       integer;
  128.    incnt:       integer;
  129.    pc:          byte;
  130.    pcbits:      byte;
  131.    pcbitv:      byte;
  132.    zipfd:       dos_handle;
  133.    zipfn:       dos_filename;
  134.  
  135.  
  136.  
  137. (* ----------------------------------------------------------- *)
  138. (*
  139.  * output stream variables
  140.  *
  141.  *)
  142.  
  143. var
  144.    outbuf:      array[0..4096] of byte; {for rle look-back}
  145.    outpos:      longint;                {absolute position in outfile}
  146.    outcnt:      integer;
  147.    outfd:       dos_handle;
  148.    filename:    string;
  149.    extra:       string;
  150.  
  151.  
  152.  
  153. (* ----------------------------------------------------------- *)
  154.  
  155. type
  156.    Sarray = array[0..255] of string[64];
  157.  
  158. var
  159.    factor:     integer;
  160.    followers:  Sarray;
  161.    ExState:    integer;
  162.    C:          integer;
  163.    V:          integer;
  164.    Len:        integer;
  165.  
  166. const
  167.    hsize =     8192;
  168.  
  169. type
  170.    hsize_array_integer = array[0..hsize] of integer;
  171.    hsize_array_byte    = array[0..hsize] of byte;
  172.  
  173. var
  174.    prefix_of:  hsize_array_integer;
  175.    suffix_of:  hsize_array_byte;
  176.    stack:      hsize_array_byte;
  177.    stackp:     integer;
  178.  
  179.  
  180.  
  181. (*
  182.  * Zipfile input/output handlers
  183.  *
  184.  *)
  185.  
  186.  
  187. (* ------------------------------------------------------------- *)
  188. procedure skip_csize;
  189. begin
  190.    dos_lseek(zipfd,csize,seek_cur);
  191.    zipeof := true;
  192.    csize := 0;
  193.    incnt := 0;
  194. end;
  195.  
  196.  
  197. (* ------------------------------------------------------------- *)
  198. procedure ReadByte(var x: byte);
  199. begin
  200.    if incnt = 0 then
  201.    begin
  202.       if csize = 0 then
  203.       begin
  204.          zipeof := true;
  205.          exit;
  206.       end;
  207.  
  208.       inpos := sizeof(inbuf);
  209.       if inpos > csize then
  210.          inpos := csize;
  211.       incnt := dos_read(zipfd,inbuf,inpos);
  212.  
  213.       inpos := 1;
  214.       dec(csize,incnt);
  215.    end;
  216.  
  217.    x := inbuf[inpos];
  218.    inc(inpos);
  219.    dec(incnt);
  220. end;
  221.  
  222.  
  223. (* ------------------------------------------------------------- *)
  224. procedure ReadBits(bits: integer; var result: integer);
  225.    {read the specified number of bits}
  226. const
  227.    bit:     integer = 0;
  228.    bitv:    integer = 0;
  229.    x:       integer = 0;
  230. begin
  231.    x := 0;
  232.    bitv := 1;
  233.  
  234.    for bit := 0 to bits-1 do
  235.    begin
  236.  
  237.       if pcbits > 0 then
  238.       begin
  239.          dec(pcbits);
  240.          pcbitv := pcbitv shl 1;
  241.       end
  242.       else
  243.  
  244.       begin
  245.          ReadByte(pc);
  246.          pcbits := 7;
  247.          pcbitv := 1;
  248.       end;
  249.  
  250.       if (pc and pcbitv) <> 0 then
  251.          x := x or bitv;
  252.  
  253.       bitv := bitv shl 1;
  254.    end;
  255.  
  256.    result := x;
  257. end;
  258.  
  259.  
  260. (* ---------------------------------------------------------- *)
  261. procedure get_string(ln: word; var s: string);
  262. var
  263.    n: word;
  264. begin
  265.    if ln > 255 then
  266.       ln := 255;
  267.    n := dos_read(zipfd,s[1],ln);
  268.    s[0] := chr(ln);
  269. end;
  270.  
  271.  
  272. (* ------------------------------------------------------------- *)
  273. procedure OutByte (c: integer);
  274.    (* output each character from archive to screen *)
  275. begin
  276.    outbuf[outcnt {outpos mod sizeof(outbuf)} ] := c;
  277.    inc(outpos);
  278.    inc(outcnt);
  279.  
  280.    if outcnt = sizeof(outbuf) then
  281.    begin
  282.       dos_write(outfd,outbuf,outcnt);
  283.       outcnt := 0;
  284.       write('.');
  285.    end;
  286. end;
  287.  
  288.  
  289. (*
  290.  * expand 'reduced' members of a zipfile
  291.  *
  292.  *)
  293.  
  294. (*
  295.  * The Reducing algorithm is actually a combination of two
  296.  * distinct algorithms.  The first algorithm compresses repeated
  297.  * byte sequences, and the second algorithm takes the compressed
  298.  * stream from the first algorithm and applies a probabilistic
  299.  * compression method.
  300.  *
  301.  *)
  302.  
  303. function reduce_L(x: byte): byte;
  304. begin
  305.    case factor of
  306.       1: reduce_L := x and $7f;
  307.       2: reduce_L := x and $3f;
  308.       3: reduce_L := x and $1f;
  309.       4: reduce_L := x and $0f;
  310.    end;
  311. end;
  312.  
  313. function reduce_F(x: byte): byte;
  314. begin
  315.    case factor of
  316.       1: if x = 127 then reduce_F := 2 else reduce_F := 3;
  317.       2: if x = 63  then reduce_F := 2 else reduce_F := 3;
  318.       3: if x = 31  then reduce_F := 2 else reduce_F := 3;
  319.       4: if x = 15  then reduce_F := 2 else reduce_F := 3;
  320.    end;
  321. end;
  322.  
  323. function reduce_D(x,y: byte): word;
  324. begin
  325.    case factor of
  326.       1: reduce_D := ((x shr 7) and $01) * 256 + Y + 1;
  327.       2: reduce_D := ((x shr 6) and $03) * 256 + Y + 1;
  328.       3: reduce_D := ((x shr 5) and $07) * 256 + Y + 1;
  329.       4: reduce_D := ((x shr 4) and $0f) * 256 + Y + 1;
  330.    end;
  331. end;
  332.  
  333. function reduce_B(x: byte): word;
  334.    {number of bits needed to encode the specified number}
  335. begin
  336.    case x-1 of
  337.       0..1:    reduce_B := 1;
  338.       2..3:    reduce_B := 2;
  339.       4..7:    reduce_B := 3;
  340.       8..15:   reduce_B := 4;
  341.      16..31:   reduce_B := 5;
  342.      32..63:   reduce_B := 6;
  343.      64..127:  reduce_B := 7;
  344.    else        reduce_B := 8;
  345.    end;
  346. end;
  347.  
  348. procedure Expand(c: byte);
  349. const
  350.    DLE = 144;
  351. var
  352.    op:   longint;
  353.    i:    integer;
  354.  
  355. begin
  356.  
  357.    case ExState of
  358.         0:  if C <> DLE then
  359.                 outbyte(C)
  360.             else
  361.                 ExState := 1;
  362.  
  363.         1:  if C <> 0 then
  364.             begin
  365.                 V := C;
  366.                 Len := reduce_L(V);
  367.                 ExState := reduce_F(Len);
  368.             end
  369.             else
  370.             begin
  371.                 outbyte(DLE);
  372.                 ExState := 0;
  373.             end;
  374.  
  375.         2:  begin
  376.                Len := Len + C;
  377.                ExState := 3;
  378.             end;
  379.  
  380.         3:  begin
  381.                op := outpos-reduce_D(V,C);
  382.                for i := 0 to Len+2 do
  383.                begin
  384.                   if op < 0 then
  385.                      outbyte(0)
  386.                   else
  387.                      outbyte(outbuf[op mod sizeof(outbuf)]);
  388.                   inc(op);
  389.                end;
  390.  
  391.                ExState := 0;
  392.             end;
  393.    end;
  394. end;
  395.  
  396.  
  397. procedure LoadFollowers;
  398. var
  399.    x: integer;
  400.    i: integer;
  401.    b: integer;
  402. begin
  403.    for x := 255 downto 0 do
  404.    begin
  405.       ReadBits(6,b);
  406.       followers[x][0] := chr(b);
  407.  
  408.       for i := 1 to length(followers[x]) do
  409.       begin
  410.          ReadBits(8,b);
  411.          followers[x][i] := chr(b);
  412.       end;
  413.    end;
  414. end;
  415.  
  416.  
  417. (* ----------------------------------------------------------- *)
  418. procedure unReduce;
  419.    {expand probablisticly reduced data}
  420.  
  421. var
  422.    lchar:   integer;
  423.    lout:    integer;
  424.    I:       integer;
  425.  
  426. begin
  427.    factor := cmethod - 1;
  428.    if (factor < 1) or (factor > 4) then
  429.    begin
  430.       skip_csize;
  431.       exit;
  432.    end;
  433.  
  434.    ExState := 0;
  435.    LoadFollowers;
  436.    lchar := 0;
  437.  
  438.    while (not zipeof) and (outpos < cusize) do
  439.    begin
  440.  
  441.       if followers[lchar] = '' then
  442.          ReadBits( 8,lout )
  443.       else
  444.  
  445.       begin
  446.          ReadBits(1,lout);
  447.          if lout <> 0 then
  448.             ReadBits( 8,lout )
  449.          else
  450.          begin
  451.             ReadBits( reduce_B(length(followers[lchar])), I );
  452.             lout := ord( followers[lchar][I+1] );
  453.          end;
  454.       end;
  455.  
  456.       if zipeof then
  457.          exit;
  458.  
  459.       Expand( lout );
  460.       lchar := lout;
  461.    end;
  462.  
  463. end;
  464.  
  465.  
  466.  
  467. (*
  468.  * expand 'shrunk' members of a zipfile
  469.  *
  470.  *)
  471.  
  472. (*
  473.  * UnShrinking
  474.  * -----------
  475.  *
  476.  * Shrinking is a Dynamic Ziv-Lempel-Welch compression algorithm
  477.  * with partial clearing.  The initial code size is 9 bits, and
  478.  * the maximum code size is 13 bits.  Shrinking differs from
  479.  * conventional Dynamic Ziv-lempel-Welch implementations in several
  480.  * respects:
  481.  *
  482.  * 1)  The code size is controlled by the compressor, and is not
  483.  *     automatically increased when codes larger than the current
  484.  *     code size are created (but not necessarily used).  When
  485.  *     the decompressor encounters the code sequence 256
  486.  *     (decimal) followed by 1, it should increase the code size
  487.  *     read from the input stream to the next bit size.  No
  488.  *     blocking of the codes is performed, so the next code at
  489.  *     the increased size should be read from the input stream
  490.  *     immediately after where the previous code at the smaller
  491.  *     bit size was read.  Again, the decompressor should not
  492.  *     increase the code size used until the sequence 256,1 is
  493.  *     encountered.
  494.  *
  495.  * 2)  When the table becomes full, total clearing is not
  496.  *     performed.  Rather, when the compresser emits the code
  497.  *     sequence 256,2 (decimal), the decompressor should clear
  498.  *     all leaf nodes from the Ziv-Lempel tree, and continue to
  499.  *     use the current code size.  The nodes that are cleared
  500.  *     from the Ziv-Lempel tree are then re-used, with the lowest
  501.  *     code value re-used first, and the highest code value
  502.  *     re-used last.  The compressor can emit the sequence 256,2
  503.  *     at any time.
  504.  *
  505.  *)
  506.  
  507. procedure unShrink;
  508.  
  509. const
  510.    max_bits =  13;
  511.    init_bits = 9;
  512.    first_ent = 257;
  513.    clear =     256;
  514.  
  515. var
  516.    cbits:      integer;
  517.    maxcode:    integer;
  518.    free_ent:   integer;
  519.    maxcodemax: integer;
  520.    offset:     integer;
  521.    sizex:      integer;
  522.    finchar:    integer;
  523.    code:       integer;
  524.    oldcode:    integer;
  525.    incode:     integer;
  526.  
  527.  
  528. (* ------------------------------------------------------------- *)
  529. procedure partial_clear;
  530. var
  531.    pr:   integer;
  532.    cd:   integer;
  533.  
  534. begin
  535.    {mark all nodes as potentially unused}
  536.    for cd := first_ent to free_ent-1 do
  537.       word(prefix_of[cd]) := prefix_of[cd] or $8000;
  538.  
  539.  
  540.    {unmark those that are used by other nodes}
  541.    for cd := first_ent to free_ent-1 do
  542.    begin
  543.       pr := prefix_of[cd] and $7fff;    {reference to another node?}
  544.       if pr >= first_ent then           {flag node as referenced}
  545.          prefix_of[pr] := prefix_of[pr] and $7fff;
  546.    end;
  547.  
  548.  
  549.    {clear the ones that are still marked}
  550.    for cd := first_ent to free_ent-1 do
  551.       if (prefix_of[cd] and $8000) <> 0 then
  552.          prefix_of[cd] := -1;
  553.  
  554.  
  555.    {find first cleared node as next free_ent}
  556.    free_ent := first_ent;
  557.    while (free_ent < maxcodemax) and (prefix_of[free_ent] <> -1) do
  558.       inc(free_ent);
  559. end;
  560.  
  561.  
  562. (* ------------------------------------------------------------- *)
  563. begin
  564.    (* decompress the file *)
  565.    maxcodemax := 1 shl max_bits;
  566.    cbits := init_bits;
  567.    maxcode := (1 shl cbits)- 1;
  568.    free_ent := first_ent;
  569.    offset := 0;
  570.    sizex := 0;
  571.  
  572.    fillchar(prefix_of,sizeof(prefix_of),$FF);
  573.    for code := 255 downto 0 do
  574.    begin
  575.       prefix_of[code] := 0;
  576.       suffix_of[code] := code;
  577.    end;
  578.  
  579.    ReadBits(cbits,oldcode);
  580.    if zipeof then
  581.       exit;
  582.    finchar := oldcode;
  583.  
  584.    OutByte(finchar);
  585.  
  586.    stackp := 0;
  587.  
  588.    while (not zipeof) do
  589.    begin
  590.       ReadBits(cbits,code);
  591.       if zipeof then
  592.          exit;
  593.  
  594.       while (code = clear) do
  595.       begin
  596.          ReadBits(cbits,code);
  597.  
  598.          case code of
  599.             1: begin
  600.                   inc(cbits);
  601.                   if cbits = max_bits then
  602.                      maxcode := maxcodemax
  603.                   else
  604.                      maxcode := (1 shl cbits) - 1;
  605.                end;
  606.  
  607.             2: partial_clear;
  608.          end;
  609.  
  610.          ReadBits(cbits,code);
  611.          if zipeof then
  612.             exit;
  613.       end;
  614.  
  615.  
  616.       {special case for KwKwK string}
  617.       incode := code;
  618.       if prefix_of[code] = -1 then
  619.       begin
  620.          stack[stackp] := finchar;
  621.          inc(stackp);
  622.          code := oldcode;
  623.       end;
  624.  
  625.  
  626.       {generate output characters in reverse order}
  627.       while (code >= first_ent) do
  628.       begin
  629.          stack[stackp] := suffix_of[code];
  630.          inc(stackp);
  631.          code := prefix_of[code];
  632.       end;
  633.  
  634.       finchar := suffix_of[code];
  635.       stack[stackp] := finchar;
  636.       inc(stackp);
  637.  
  638.  
  639.       {and put them out in forward order}
  640.       while (stackp > 0) do
  641.       begin
  642.          dec(stackp);
  643.          OutByte(stack[stackp]);
  644.       end;
  645.  
  646.  
  647.       {generate new entry}
  648.       code := free_ent;
  649.       if code < maxcodemax then
  650.       begin
  651.          prefix_of[code] := oldcode;
  652.          suffix_of[code] := finchar;
  653.          while (free_ent < maxcodemax) and (prefix_of[free_ent] <> -1) do
  654.             inc(free_ent);
  655.       end;
  656.  
  657.  
  658.       {remember previous code}
  659.       oldcode := incode;
  660.    end;
  661.  
  662. end;
  663.  
  664.  
  665.  
  666. (*
  667.  * ProZip2.int - ZIP file interface library      (2-15-89 shs)
  668.  *
  669.  * This procedure displays the text contents of a specified archive
  670.  * file.  The filename must be fully specified and verified.
  671.  *
  672.  *)
  673.  
  674.  
  675. (* ---------------------------------------------------------- *)
  676. procedure extract_member;
  677. var
  678.    b: byte;
  679.  
  680. begin
  681.    pcbits := 0;
  682.    incnt := 0;
  683.    outpos := 0;
  684.    outcnt := 0;
  685.    zipeof := false;
  686.  
  687.    outfd := dos_create(filename);
  688.    if outfd = dos_error then
  689.    begin
  690.       writeln('Can''t create output: ', filename);
  691.       halt;
  692.    end;
  693.  
  694.    case cmethod of
  695.       0:    {stored}
  696.             begin
  697.                write(' Extract: ',filename,' ...');
  698.                while (not zipeof) do
  699.                begin
  700.                   ReadByte(b);
  701.                   OutByte(b);
  702.                end;
  703.             end;
  704.  
  705.       1:    begin
  706.                write('UnShrink: ',filename,' ...');
  707.                UnShrink;
  708.             end;
  709.  
  710.       2..5: begin
  711.                write('  Expand: ',filename,' ...');
  712.                UnReduce;
  713.             end;
  714.  
  715.       else  write('Unknown compression method.');
  716.    end;
  717.  
  718.    if outcnt > 0 then
  719.       dos_write(outfd,outbuf,outcnt);
  720.  
  721.    dos_file_times(outfd,time_set,ctime,cdate);
  722.    dos_close(outfd);
  723.  
  724.    writeln('  done.');
  725. end;
  726.  
  727.  
  728. (* ---------------------------------------------------------- *)
  729. procedure process_local_file_header;
  730. var
  731.    n:             word;
  732.    rec:           local_file_header;
  733.  
  734. begin
  735.    n := dos_read(zipfd,rec,sizeof(rec));
  736.    get_string(rec.filename_length,filename);
  737.    get_string(rec.extra_field_length,extra);
  738.    csize := rec.compressed_size;
  739.    cusize := rec.uncompressed_size;
  740.    cmethod := rec.compression_method;
  741.    ctime := rec.last_mod_file_time;
  742.    cdate := rec.last_mod_file_date;
  743.    extract_member;
  744. end;
  745.  
  746.  
  747. (* ---------------------------------------------------------- *)
  748. procedure process_central_file_header;
  749. var
  750.    n:             word;
  751.    rec:           central_directory_file_header;
  752.    filename:      string;
  753.    extra:         string;
  754.    comment:       string;
  755.  
  756. begin
  757.    n := dos_read(zipfd,rec,sizeof(rec));
  758.    get_string(rec.filename_length,filename);
  759.    get_string(rec.extra_field_length,extra);
  760.    get_string(rec.file_comment_length,comment);
  761. end;
  762.  
  763.  
  764. (* ---------------------------------------------------------- *)
  765. procedure process_end_central_dir;
  766. var
  767.    n:             word;
  768.    rec:           end_central_dir_record;
  769.    comment:       string;
  770.  
  771. begin
  772.    n := dos_read(zipfd,rec,sizeof(rec));
  773.    get_string(rec.zipfile_comment_length,comment);
  774. end;
  775.  
  776.  
  777. (* ---------------------------------------------------------- *)
  778. procedure process_headers;
  779. var
  780.    sig:  longint;
  781.  
  782. begin
  783.    dos_lseek(zipfd,0,seek_start);
  784.  
  785.    while true do
  786.    begin
  787.       if dos_read(zipfd,sig,sizeof(sig)) <> sizeof(sig) then
  788.          exit
  789.       else
  790.  
  791.       if sig = local_file_header_signature then
  792.          process_local_file_header
  793.       else
  794.  
  795.       if sig = central_file_header_signature then
  796.          process_central_file_header
  797.       else
  798.  
  799.       if sig = end_central_dir_signature then
  800.       begin
  801.          process_end_central_dir;
  802.          exit;
  803.       end
  804.  
  805.       else
  806.       begin
  807.          writeln('Invalid Zipfile Header');
  808.          exit;
  809.       end;
  810.    end;
  811.  
  812. end;
  813.  
  814.  
  815. (* ---------------------------------------------------------- *)
  816. procedure extract_zipfile;
  817. begin
  818.    zipfd := dos_open(zipfn,open_read);
  819.    if zipfd = dos_error then
  820.       exit;
  821.  
  822.    process_headers;
  823.  
  824.    dos_close(zipfd);
  825. end;
  826.  
  827.  
  828. (*
  829.  * main program
  830.  *
  831.  *)
  832.  
  833. begin
  834.    writeln;
  835.    writeln(version);
  836.    writeln('Courtesy of:  S.H.Smith  and  The Tool Shop BBS,  (602) 279-2673.');
  837.    writeln;
  838.    if paramcount <> 1 then
  839.    begin
  840.       writeln('Usage:  UnZip FILE[.zip]');
  841.       halt;
  842.    end;
  843.  
  844.    zipfn := paramstr(1);
  845.    if pos('.',zipfn) = 0 then
  846.       zipfn := zipfn + '.ZIP';
  847.  
  848.    extract_zipfile;
  849. end.
  850.  
  851.  
  852.